/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../macro/head.h"
#include "ssl_impl.h"
#include "ssl_mgr.h"
#include "mgr.h"

namespace xos_encrypt
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static ssl_impl::ITEM_DATA xos_item_data;

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    ssl_impl::ssl_impl()
    {
        init_data();
    }

    ssl_impl::~ssl_impl()
    {
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int ssl_impl::get_xos_item_data( ITEM_DATA *& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::set_mgr( ssl_mgr * pMgr )
    {
        int ret = 0;
        if( pMgr->m_bServer )
        {
            m_eState = STATE_WAIT_FOR_CONNECT;
        }
        else
        {
            m_eState = STATE_CONNECT;
        }
        m_pMgr = pMgr;
        return ret;
    }

    int ssl_impl::init()
    {
        int ret = 0;
        return ret;
    }

    int ssl_impl::term()
    {
        int ret = 0;
        if( m_pSSL )
        {
            SSL_free( m_pSSL );
            m_pSSL = 0;
        }
        term_mem( &m_write_buf );
        term_mem( &m_read_buf );
        init_data();
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // return :
    //    0: no error
    //   -1: err. 
    // 
    int ssl_impl::write_error( int nRet )
    {
        int ret = -1;

        int nErr = SSL_get_error( m_pSSL, nRet );

        if( nRet < 0 )
        {
            if( SSL_ERROR_WANT_WRITE == nErr )
            {
                ret = 0;
            }
        }

        return ret;
    }

    //  0 : 成功
    // -1 : 错误
    // -2 : 关闭
    int ssl_impl::read_error( int nRet ) const
    {
        int ret = -1;

        int nErr = SSL_get_error( m_pSSL, nRet );

        if( 0 == nRet )
        {
            if( SSL_ERROR_ZERO_RETURN == nErr )
            {
                ret = -2;
            }
        }
        else if( nRet < 0 )
        {
            if( SSL_ERROR_WANT_READ == nErr )
            {
                ret = 0;
            }
            else if( SSL_ERROR_SSL == nErr )
            {
                ret = -1;
            }
        }

        return ret;
    }

    /*
    ERR_lib_error_string（）库函数在哪个库中
    ERR_func_error_string（）// 函数名
    ERR_reason_error_string（）// 出错的具体原因
    */
    int ssl_impl::show_error_info( int nRet ) const
    {
        int ret = 0;
        //int nErr = ERR_get_error();
        int nErr = SSL_get_error( m_pSSL, nRet );
        char info[4096] = { 0 };
        ERR_error_string( nErr, info );
        LOG4( "openssl error : code = %d,%d, info = %s", nRet, nErr, info );
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init_mem( BUF_MEM * pMem )
    {
        int ret = 0;
        xos_box::i_buf *& pBuf = ( pMem == &m_write_buf ) ? m_pWriteBuf : m_pReadBuf;
        if( !pMem->data )
        {
            pBuf = mgr::container()->box()->buf();
            pMem->max = (size_t)pBuf->get_buf_size( 0 );
            pMem->data = pBuf->get_data( 0, 0, 0, 0 );
            pMem->length = (size_t)pBuf->get_len( 0 );
        }
        return ret;
    }

    bool ssl_impl::empty_mem( BUF_MEM * pMem )
    {
        BIO * pBIO = ( pMem == &m_write_buf ) ? m_pWriteBIO : m_pReadBIO;
        int len = BIO_ctrl_pending( pBIO );
        return 0 == len;
    }

    int ssl_impl::term_mem( BUF_MEM * pMem )
    {
        int ret = 0;
        xos_box::i_buf *& pBuf = ( pMem == &m_write_buf ) ? m_pWriteBuf : m_pReadBuf;
        {
            xos_stl::release_interface( pBuf );
            init_null_mem( pMem );
        }
        return ret;
    }

    int ssl_impl::init_null_mem( BUF_MEM * pMem )
    {
        int ret = 0;
        pMem->max = 0;
        pMem->length = 0;
        pMem->data = 0;
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init_data()
    {
        int ret = 0;
        init_null_mem( &m_write_buf );
        init_null_mem( &m_read_buf );
        m_eState = STATE_NONE;
        m_pWriteBuf = 0;
        m_pReadBuf = 0;
        m_pWriteBIO = 0;
        m_pReadBIO = 0;
        m_pSSL = 0;
        // mgr
        m_pMgr = 0;
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // decode方法说明：
    // 
    //  1 : 初始状态，是接收对方shakehand包的。此时生成的返回握手包在pSendBack中，需立刻发给对方
    //  2 : 正常状态，解密，生成数据在pOut中。
    //  3 : 正常状态，收到对方的close_notify，此时生成返回确认包，在pSendBack中，需立刻发给对方
    // 
    // 返回值：
    // 
    //  0 : 解析数据成功
    //  1 : 连接成功
    //  2 : 连接断开
    // -1 : 连接失败，协议或数据错误。服务端接受连接时才会发生，此时会保留全部数据，以便可以利用它来切换到非ssl的明文协议状态
    // -2 : 连接需要更多数据
    // -3 : 解析失败，协议或数据错误
    // -4 : 解析需要更多数据
    // -5 : 写入BIO失败，不太可能
    int ssl_impl::decode( xos_box::i_list * pIn, xos_box::i_list * pOut, xos_box::i_list * pSendBack )
    {
        xos_box::i_buf * pBuf = 0;
        int nResult = 0;
        int ret = 0;

        {
            init_mem( &m_write_buf );
            init_mem( &m_read_buf );
        }

        while( ( 0 == ret ) && ( pBuf = (xos_box::i_buf *)pIn->front( 0 ) ) )
        {
            char * lpszBuf = pBuf->get_data( 0, 0, 0, 0 );
            int nLen = pBuf->get_len( 0 );
            int nPos = pBuf->get_pos( 0 );
            int nBIO = BIO_write( m_pReadBIO, lpszBuf, nLen );
            // 如果是握手阶段
            if( ( 0 == ret ) && ( ( STATE_WAIT_FOR_CONNECT == m_eState ) || ( STATE_CONNECT == m_eState ) ) )
            {
                int r = SSL_do_handshake( m_pSSL );
                if( 1 == r )
                {
                    LOG4( "hand ok, state = %s", SSL_state_string( m_pSSL ) );
                    m_eState = STATE_NORMAL;
                    show_error_info( r );
                    nResult = 1;
                }
                else
                {
                    int rr = read_error( r );
                    if( -1 == rr )
                    {
                        nResult = -1;
                        ret = 1;
                    }
                    else
                    {
                        nResult = -2;
                    }
                }
                if( 0 == ret )
                {
                    get_data_from_write_bio( pSendBack );
                }
            }
            // 正常数据解密
            if( ( 0 == ret ) && ( STATE_NORMAL == m_eState ) )
            {
                xos_box::i_buf * pB = mgr::container()->box()->buf();
                char * lpszBuf = pB->get_data( 0, 0, 0, 0 );
                int nSize = pB->get_buf_size( 0 );
                int len = SSL_read( m_pSSL, lpszBuf, nSize );
                if( len > 0 )
                {
                    pB->set_len( len );
                    pOut->push_back( pB );
                    nResult = 0;
                    pB = 0;
                }
                else
                {
                    int r = read_error( len );
                    // 对方断开了ssl连接
                    if( -2 == r )
                    {
                        SSL_shutdown( m_pSSL );
                        get_data_from_write_bio( pSendBack );
                        m_eState = STATE_SHUTDOWN;
                        nResult = 2;
                        ret = 1;
                    }
                    // 解析数据错误
                    else if( -1 == r )
                    {
                        nResult = -3;
                        ret = 1;
                    }
                    // 需要更多数据
                    else if( 0 == nResult )
                    {
                        nResult = -4;
                    }
                }
                xos_stl::release_interface( pB );
            }
            // 连接失败时会保留全部数据。这样可以转为非ssl的明文协议模式再解析数据
            if( -1 != nResult )
            {
                // 所有数据都写入了BIO
                if( nBIO == nLen )
                {
                    xos_stl::release_interface( pBuf );
                    pIn->pop_front();
                }
                // 写入了部分数据
                else if( nBIO > 0 )
                {
                    nLen -= nBIO;
                    nPos += nBIO;
                    pBuf->set_len( nLen );
                    pBuf->set_pos( nPos );
                }
                // 写入BIO失败，不可能
                else
                {
                    nResult = -5;
                    ret = 1;
                }
            }
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return nResult;
    }

    // 
    // encode说明：
    // 
    //  1 : 初始状态，且pIn为空，表示想建立连接，pOut中为生成的shakehand包
    //  2 : 正常连接，且pIn非空，当然是加密数据，pOut中为加密后的数据
    //  3 : 正常连接，且pIn为空，表示要断开连接，pOut中为生成的shutdown包
    //  4 : 生成的数据包都直接发送给对方
    // 
    // 返回值：
    // 
    //  0 : 成功，可能是：生成shakehand包成功，加密数据成功，生成shutdown包成功。
    // -1 : 失败，不可能出现失败，这个方法永远成功
    // 
    int ssl_impl::encode( xos_box::i_list * pIn, xos_box::i_list * pOut )
    {
        xos_box::i_buf * pBuf = 0;
        int nResult = 0;
        int nNum = 0;
        int ret = 0;

        {
            init_mem( &m_write_buf );
            init_mem( &m_read_buf );
        }

        while( ( 0 == ret ) && ( pBuf = (xos_box::i_buf *)pIn->front( 0 ) ) )
        {
            if( 0 == ret )
            {
                const char * lpszBuf = pBuf->get_data( 0, 0, 0, 0 );
                int nSize = pBuf->get_buf_size( 0 ) / 2;
                int nLen = pBuf->get_len( 0 );
                int nPos = pBuf->get_pos( 0 );
                int nUseLen = nLen > nSize ? nSize : nLen;
                int len = SSL_write( m_pSSL, lpszBuf, nUseLen );
                if( len > 0 )
                {
                    nLen -= len;
                    nPos += len;
                }
                if( 0 == nLen )
                {
                    xos_stl::release_interface( pBuf );
                    pIn->pop_front();
                }
                else
                {
                    pBuf->set_len( nLen );
                    pBuf->set_pos( nPos );
                }
                if( len <= 0 )
                {
                    int r = write_error( len );
                    if( -1 == r )
                    {
                        nResult = -1;
                        ret = 1;
                    }
                }
            }
            if( 0 == ret )
            {
                int r = get_data_from_write_bio( pOut );
                if( 0 != r )
                {
                    nResult = -1;
                    ret = 1;
                }
            }
            if( 0 == ret )
            {
                nNum++;
            }
        }

        if( ( 0 == ret ) && ( 0 == nNum ) )
        {
            // 要建立连接
            if( STATE_CONNECT == m_eState )
            {
                SSL_do_handshake( m_pSSL );
            }
            // 要断开连接
            else if( STATE_NORMAL == m_eState )
            {
                SSL_shutdown( m_pSSL );
            }
            // 不可能啊，服务端连接前瞎发什么空内容
            else
            {
                nResult = -1;
                ret = 1;
            }
            get_data_from_write_bio( pOut );
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return nResult;
    }

    int ssl_impl::get_data_from_write_bio( xos_box::i_list * pOut )
    {
        int ret = 0; 
        
        BIO * pBIO = m_pWriteBIO;
        int nLen = (int)BIO_ctrl_pending( pBIO );

        if( nLen > 0 )
        {
            xos_box::i_buf * pB = mgr::container()->box()->buf();
            char * lpszBuf = pB->get_data( 0, 0, 0, 0 );
            int nSize = pB->get_buf_size( 0 );
            int len = BIO_read( pBIO, lpszBuf, nSize );
            if( len > 0 )
            {
                pB->set_len( len );
                pOut->push_back( pB );
                pB = 0;
            }
            xos_stl::release_interface( pB );
        }
        else
        {
            ret = 1;
        }

        return ret;
    }

    // 初始化
    int ssl_impl::init_ssl()
    {
        int ret = 0;

        if( 0 == ret )
        {
            SSL_CTX * pCtx = m_pMgr->m_pCtx;
            m_pSSL = SSL_new( pCtx );
            if( !m_pSSL )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            m_pWriteBIO = BIO_new( BIO_s_mem() );
            if( !m_pWriteBIO )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            m_pReadBIO = BIO_new( BIO_s_mem() );
            if( !m_pReadBIO )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            BIO_set_mem_buf( m_pWriteBIO, &m_write_buf, BIO_NOCLOSE );
            BIO_set_mem_buf( m_pReadBIO, &m_read_buf, BIO_NOCLOSE );
            SSL_set_bio( m_pSSL, m_pReadBIO, m_pWriteBIO );
        }

        if( 0 == ret )
        {
            if( !m_pMgr->m_bServer )
            {
                if( !m_pMgr->m_server_domain.empty() )
                {
                    SSL_set_tlsext_host_name( m_pSSL, m_pMgr->m_server_domain.c_str() );
                }
                SSL_set_connect_state( m_pSSL );
            }
            else
            {
                SSL_set_accept_state( m_pSSL );
            }
        }

        return ret;
    }

} // xos_encrypt
